﻿/*
 * File: BaseMenuScene.cs
 * Author: Gourav Das
 * Purpose: Abstract Base class for Menus
 * Created: April 30th 2011
 * Copyright: Gourav Das, 2011
 */

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using _22yards._2D;
using _22yards.Scenes;
using _22yards.Core;
using _22yards;

namespace _22yards.Scenes.Helpers
{
    /// <summary>
    /// Base class for screens that contain a menu. 
    /// </summary>
    abstract class BaseMenuScene : Scene
    {
        MenuItem menuItem;
        List<MenuItem> menuItems = new List<MenuItem>();
        int selectedItem = 0;
        string menuTitle;

        /// <summary>
        /// Gets the list of menu entries, so derived classes can add or change the menu contents.
        /// </summary>
        protected IList<MenuItem> MenuItems
        {
            get { return menuItems; }
        }

        /// <summary>
        /// Gets or sets the index of selected menu item.
        /// </summary>
        /// <value>
        /// The selected item.
        /// </value>
        public int SelectedItem
        {
            get { return selectedItem; }
            protected set { selectedItem = value; }
        }

        protected string MenuTitle
        {
            get { return menuTitle; }
            private set { menuTitle = value; }
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        public BaseMenuScene(string _menuTitle)
        {
            this.menuTitle = _menuTitle;

            TransitioningTime = TimeSpan.FromSeconds(0.5);
            TransitionedTime = TimeSpan.FromSeconds(0.5);
        }

        /// <summary>
        /// Returns the index of the menu entry at the position of the given mouse state.
        /// </summary>
        /// <returns>Index of menu entry if valid, -1 otherwise</returns>
        protected int GetMenuEntryAt(Vector2 position)
        {
            int index = 0;
            foreach (MenuItem item in MenuItems)
            {
                float width = item.boundinBox.width;
                float height = item.boundinBox.height;
                Rectangle rect = new Rectangle((int)(item.Position.X),
                                               (int)(item.Position.Y - height / 2f),
                                               (int)width, (int)height);
                if (rect.Contains((int)position.X, (int)position.Y))
                {
                    return index;
                }
                ++index;
            }
            return -1;
        }

        /// <summary>
        /// Responds to user input, moving through menu items and accepting or cancelling the menu.
        /// </summary>
        /// <param name="_input"></param>
        public override void HandleInput(InputEngine _input)
        {

            // Accept or cancel the menu? We pass in our ControllingPlayer,
            PlayerIndex playerIndex;

            int hoverIndex = GetMenuEntryAt(new Vector2(_input.LastMouseStates.X, _input.LastMouseStates.Y));
            if (hoverIndex > -1 && _input.MousePositionChanged(ControllingPlayer, out playerIndex))// IsMouseLeftButtonPress(ControllingPlayer,out playerIndex))
            {
                SelectedItem = hoverIndex;
            }
            else if (hoverIndex < 0 && _input.MousePositionChanged(ControllingPlayer, out playerIndex))// IsMouseLeftButtonPress(ControllingPlayer,out playerIndex))
            {
                SelectedItem = -1;
            }
            else //if (!_input.MousePositionChanged(ControllingPlayer, out playerIndex) && hoverIndex <0)
            {
                // Move to the previous menu item
                if (_input.IsMenuUp(ControllingPlayer) && !_input.MousePositionChanged(ControllingPlayer, out playerIndex))
                {
                    SelectedItem--;

                    //Cycle through Menu Items
                    if (SelectedItem < 0)
                        SelectedItem = menuItems.Count - 1;
                }

                // Move to the next menu item
                if (_input.IsMenuDown(ControllingPlayer) && !_input.MousePositionChanged(ControllingPlayer, out playerIndex))
                {
                    SelectedItem++;

                    //Cycle through Menu Items
                    if (SelectedItem >= menuItems.Count)
                        SelectedItem = 0;
                }

                if (_input.IsMenuSelect(ControllingPlayer, out playerIndex) && !_input.MousePositionChanged(ControllingPlayer, out playerIndex))
                {
                    OnSelectEntry(SelectedItem, playerIndex);
                }
                else if (_input.IsMenuCancel(ControllingPlayer, out playerIndex) && !_input.MousePositionChanged(ControllingPlayer, out playerIndex))
                {
                    OnCancel(playerIndex);
                }
            }
        }


        /// <summary>
        /// Handler for when the user has chosen a menu item.
        /// </summary>
        /// <param name="_entryIndex">Index of the entry.</param>
        /// <param name="_playerIndex">Index of the player.</param>
        protected virtual void OnSelectEntry(int _entryIndex, PlayerIndex _playerIndex)
        {
            if(_entryIndex>-1)
                menuItems[_entryIndex].OnSelectEntry(_playerIndex);
        }


        /// <summary>
        /// Handler for when the user has cancelled the menu.
        /// </summary>
        /// <param name="_playerIndex">Index of the player.</param>
        protected virtual void OnCancel(PlayerIndex _playerIndex)
        {
            ExitScene();
        }


        /// <summary>
        /// Helper overload to use OnCancel as a MenuItem event handler.
        /// </summary>
        protected void OnCancel(object sender, PlayerIndexEventArgs e)
        {
            OnCancel(e.PlayerIndex);
        }


        /// <summary>
        /// Allows the scene to position the menu items. By default
        /// all menu items are lined up in a vertical list, centered on the screen.
        /// </summary>
        protected virtual void UpdateMenuItemLocations()
        {
            // Make the menu slide into place during transitions, using a
            // power curve to make things look more interesting (this makes
            // the movement slow down as it nears the end).
            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);

            // start at Y = 175; each X value is generated per entry
            Vector2 position = new Vector2(0f, ((float)175 * Game22Yards.scaleFactor.widthScale));

            // update each menu entry's location in turn
            for (int i = 0; i < menuItems.Count; i++)
            {
                float prev = 0f;
                menuItem = menuItems[i];

                if (menuItem.DisplayOnLeftCorner)
                {
                    // each Item is to be left corner
                    position.X = sceneEngine.GraphicsDevice.Viewport.Width / 16 - menuItem.GetWidth(this) / 2;

                    if (SceneState == enumSceneState.Transitioning)
                        position.X -= transitionOffset * 256;
                    else
                        position.X += transitionOffset * 512;

                    //save state of Y
                    prev = position.Y;

                    // each Item is to be bottom
                    position.Y = sceneEngine.GraphicsDevice.Viewport.Height - sceneEngine.GraphicsDevice.Viewport.Height / 16 - menuItem.GetHeight(this) / 2;

                    if (SceneState == enumSceneState.Transitioning)
                        position.Y -= transitionOffset * 256;
                    else
                        position.Y += transitionOffset * 512;

                    menuItem.Position = position;
                }
                else if (menuItem.DisplayOnRightCorner)
                {
                    // each Item is to be on right corner
                    position.X = sceneEngine.GraphicsDevice.Viewport.Width - sceneEngine.GraphicsDevice.Viewport.Width / 16 - menuItem.GetWidth(this) / 2;

                    if (SceneState == enumSceneState.Transitioning)
                        position.X -= transitionOffset * 256;
                    else
                        position.X += transitionOffset * 512;

                    //save state of Y before changing it
                    prev = position.Y;

                    // each Item is to be on bottom corner
                    position.Y = sceneEngine.GraphicsDevice.Viewport.Height - sceneEngine.GraphicsDevice.Viewport.Height / 16 - menuItem.GetHeight(this) / 2;

                    if (SceneState == enumSceneState.Transitioning)
                        position.Y -= transitionOffset * 256;
                    else
                        position.Y += transitionOffset * 512;

                    menuItem.Position = position;
                }

                else
                {
                    // each Item is to be centered horizontally
                    position.X = sceneEngine.GraphicsDevice.Viewport.Width / 2 - menuItem.GetWidth(this) / 2;

                    if (SceneState == enumSceneState.Transitioning)
                        position.X -= transitionOffset * 256;
                    else
                        position.X += transitionOffset * 512;

                    prev = position.Y;

                    // set the Item's position
                    menuItem.Position = position;

                }

                // move down for the next Item the size of this entry
                position.Y = prev + menuItem.GetHeight(this);

                //Bounding box of this menu item
                menuItem.boundinBox.height = menuItem.GetHeight(this);
                menuItem.boundinBox.width = menuItem.GetWidth(this);
            }

            
        }


        /// <summary>
        /// Updates the menu.
        /// </summary>
        /// <param name="_gameTime">The game time.</param>
        /// <param name="_otherScreenHasFocus">if set to <c>true</c> [other screen has focus].</param>
        /// <param name="_coveredByOtherScreen">if set to <c>true</c> [covered by other screen].</param>
        public override void Update(GameTime _gameTime, bool _otherScreenHasFocus,
                                                       bool _coveredByOtherScreen)
        {
            base.Update(_gameTime, _otherScreenHasFocus, _coveredByOtherScreen);

            // Update each menu item.
            for (int i = 0; i < menuItems.Count; i++)
            {
                bool isSelected = IsActive && (i == selectedItem);

                menuItems[i].Update(this, isSelected, _gameTime);
            }
        }


        /// <summary>
        /// Draws the menu.
        /// </summary>
        /// <param name="_gameTime"></param>
        public override void Draw(GameTime _gameTime)
        {
            // make sure our Items are in the right place before we draw them
            UpdateMenuItemLocations();

            sceneEngine.SpriteBatch.Begin();

            // Draw each menu item in turn.
            for (int i = 0; i < menuItems.Count; i++)
            {
                MenuItem menuItem = menuItems[i];

                bool isSelected = IsActive && (i == selectedItem);

                menuItem.Draw(this, isSelected, _gameTime);
            }

            // Make the menu title slide into place during transitions, this makes
            // the movement slow down as it nears the end
            float transitionOffset = (float)Math.Pow(TransitionPosition, 2);

            // Draw the menu title centered on the screen
            Vector2 titlePosition = new Vector2(sceneEngine.GraphicsDevice.Viewport.Width / 2, ((float)80 * Game22Yards.scaleFactor.htScale));
            Vector2 titleOrigin = sceneEngine.TxtNFont.spFontGoodTimes.MeasureString(menuTitle) / 2;
            Color titleColor = Color.White;
            float titleScale = 1.50f;

            titlePosition.Y -= transitionOffset * 100;

            sceneEngine.SpriteBatch.DrawString(sceneEngine.TxtNFont.spFontGoodTimes, menuTitle, titlePosition, titleColor, 0,
                                   titleOrigin, titleScale, SpriteEffects.None, 0);

            sceneEngine.SpriteBatch.End();
        }

    }
}
